#include "sct/encode/audioenc.h"

namespace sct
{
class CPCMFormatMap
{
public:
	PCMFormat format;
	AVSampleFormat av_format;
};

static CPCMFormatMap gFMTMap[] =
{
		{PCM_FMT_S16, AV_SAMPLE_FMT_S16},
};

static int getAVFormat(PCMFormat fmt)
{
	int sz = sizeof(gFMTMap)/sizeof(gFMTMap[0]);
	for(int i = 0; i<sz; i++)
	{
		if(gFMTMap[i].format == fmt) return gFMTMap[i].av_format;
	}

	SCT_LOGD("not found format");

	return -1;
}

class CPCMChannelMap
{
public:
	PCMChannel channel;
	int av_channel;
};

static CPCMChannelMap gCHMap[] =
{
		{PCM_CH_STEREO, AV_CH_LAYOUT_STEREO,},
		{PCM_CH_MONO, AV_CH_LAYOUT_MONO,},
};

static int getAVChannelLayout(PCMChannel ch)
{
	int sz = sizeof(gCHMap)/sizeof(gCHMap[0]);
	for(int i = 0; i<sz; i++)
	{
		if(gCHMap[i].channel == ch) return gCHMap[i].av_channel;
	}

	SCT_LOGD("not found ch");

	return -1;
}

void CAudioEnc::init(const CEncodeParam & param)
{
	mEncodeParam = param;

	///find encoder
	switch(mCodecType)
	{
	case CT_AAC:
	{
		mCodec = avcodec_find_encoder(CODEC_ID_AAC);

		break;
	}

	default:
	{
		SCT_LOGD();
		break;
	}
	}

	if(!mCodec)
	{
		SCT_LOGD("codec:%d not found!", mCodecType);
		exit(1);
	}

	mCodecCtx = avcodec_alloc_context3(mCodec);
	mFrame = avcodec_alloc_frame();

	AVRational rate;
	rate.num = 1;

	mCodecCtx->bit_rate = mEncodeParam.bitrate;

	mCodecCtx->sample_fmt = (AVSampleFormat)getAVFormat(mEncodeParam.sample_fmt);
	mCodecCtx->sample_rate = mEncodeParam.sample_rate;
	mCodecCtx->channel_layout = getAVChannelLayout(mEncodeParam.channel_layout);
	mCodecCtx->frame_size = mEncodeParam.sample_num_in_frame;
	mCodecCtx->channels = av_get_channel_layout_nb_channels(mCodecCtx->channel_layout);

	rate.den = mEncodeParam.sample_rate;
	switch(mCodecType)
	{
	case CT_AAC:
	{
		mCodecCtx->profile = FF_PROFILE_AAC_LOW;

		break;
	}

	default:
	{
		SCT_LOGD();
		break;
	}
	}

	mCodecCtx->time_base = rate;

	if(avcodec_open2(mCodecCtx, mCodec, NULL)<0)
	{
		SCT_LOGD("open codec failed");
		exit(2);
	}

	mEncBuffSz = av_samples_get_buffer_size(NULL,
			mCodecCtx->channels,
			mCodecCtx->frame_size,
			mCodecCtx->sample_fmt, 1);

	mSwapBuff = new uint8_t[mEncBuffSz];

	mFrame->nb_samples = mCodecCtx->frame_size;
	mFrame->format = mCodecCtx->sample_fmt;
	if( avcodec_fill_audio_frame(mFrame,
			mCodecCtx->channels,
			mCodecCtx->sample_fmt,
			mSwapBuff,
			mEncBuffSz,
			1) < 0)
	{
		SCT_LOGD("avcodec_fill_audio_frame failed");
		exit(9);
	}

}

void CAudioEnc::_encodeOneFrame()
{
	AVPacket avpkt;
	av_init_packet(&avpkt);
	avpkt.data = NULL;
	avpkt.size = 0;

	int got_packet_ptr = 0;

	int ret = avcodec_encode_audio2(mCodecCtx, &avpkt, mFrame, &got_packet_ptr);
	if(ret < 0)
	{
		SCT_LOGD("encode one audio frame error!");
	}

	if(got_packet_ptr)
	{

		int64_t t = avpkt.pts;
		t *= 1000000;
		t /= mEncodeParam.sample_rate;

		CBlockInfo info;
		info.pts = t;
		mCallback->onGenData(this, avpkt.data, avpkt.size, info);

		av_free_packet(&avpkt);
	}
}

void CAudioEnc::encodeOneFrame(const int64_t pts, const void* buf, const int len0)
{
	int64_t t = pts;
	t = pts * mEncodeParam.sample_rate;
	t /= 1000000;

	mFrame->pts =  t;

	const uint8_t *pdata = (const uint8_t*)buf;
	int len = len0;

	if(mSwapBuffLen)
	{
		int cpsz = mEncBuffSz-mSwapBuffLen;
		memcpy(mSwapBuff+mSwapBuffLen, pdata, cpsz);

		pdata += cpsz;
		len -= cpsz;

		mFrame->data[0] = mSwapBuff;
		_encodeOneFrame();
	}


	int n = len/mEncBuffSz;
	int i = 0;
	for(i = 0; i<n; i++)
	{
		mFrame->data[0] = (uint8_t*)pdata + i*mEncBuffSz;

		_encodeOneFrame();
	}

	int left = len - n*mEncBuffSz;
	if(left)
	{
		memcpy(mSwapBuff, pdata + n*mEncBuffSz, left);
	}
	mSwapBuffLen = left;
}

CAudioEnc::CAudioEnc(const CodecType codecType):CEncodeInterface(codecType),
		mFrame(NULL),
		mCodec(NULL),
		mCodecCtx(NULL),
		mEncBuffSz(0),
		mSwapBuff(NULL),
		mSwapBuffLen(0)
{
	bzero(&mEncodeParam, sizeof(mEncodeParam));
}

CAudioEnc::~CAudioEnc()
{
	if(mFrame)
	{
		av_frame_free(&mFrame);
	}

	if(mCodecCtx)
	{
		avcodec_free_context(&mCodecCtx);
	}

	if(mSwapBuff)
	{
		SCT_DELETE_ARRAY(mSwapBuff);
	}
}
}
